#include "ornament.h"
#include "design_mode_examples.h"
#include "hash-list.h"
#include "graph_and_tree.h"
#include <iostream>
#include <map>  // for baseline.
#include <cstdlib>

using namespace std;
using namespace DesignMode;
using namespace kaldi;



template<class Int, class T> 
void TestHashList() {
	typedef typename HashList<Int, T>::Elem Elem;

	HashList<Int, T> hash;
	hash.SetSize(200);  // must be called before use.
	std::map<Int, T> m1;
	Int prev_key = -1;
	for (size_t j = 0; j < 50; j++) {
		Int key = Rand() % 200;
		T val = Rand() % 50;
		m1[key] = val;
		if (prev_key == key)
		{
			std::cout << "Find same key here\n";
		}
		Elem *e = hash.Find(key);
		if (e) e->val = val;
		else  hash.Insert(key, val);
		prev_key = key;
	}


	std::map<Int, T> m2;

	for (int i = 0; i < 100; i++) {
		m2.clear();
		for (typename std::map<Int, T>::const_iterator iter = m1.begin();
			iter != m1.end();
			iter++) {
				m2[iter->first + 1] = iter->second;
		}
		std::swap(m1, m2);

		Elem *h = hash.Clear(), *tmp;

		hash.SetSize(100 + Rand() % 100);  // note, SetSize is relatively cheap
		// operation as long as we are not increasing the size more than it's ever
		// previously been increased to.

		for (; h != NULL; h = tmp) {
			hash.Insert(h->key + 1, h->val);
			tmp = h->tail;
			hash.Delete(h);  // think of this like calling delete.
		}

		// Now make sure h and m2 are the same.
		const Elem *list = hash.GetList();
		size_t count = 0;
		for (; list != NULL; list = list->tail, count++) {
			KALDI_ASSERT(m1[list->key] == list->val);
		}

		for (size_t j = 0; j < 10; j++) {
			Int key = Rand() % 200;
			bool found_m1 = (m1.find(key) != m1.end());
			if (found_m1) m1[key];
			Elem *e = hash.Find(key);
			KALDI_ASSERT((e != NULL) == found_m1);
			if (found_m1)
				KALDI_ASSERT(m1[key] == e->val);
		}

		KALDI_ASSERT(m1.size() == count);
	}
}
int HashListTest() {
	using namespace kaldi;
	for (size_t i = 0;i < 3;i++) {
		TestHashList<int, unsigned int>();
		TestHashList<unsigned int, int>();
		TestHashList<int16, int32>();
		TestHashList<int32, int16>();
		TestHashList<char, unsigned char>();
		TestHashList<unsigned char, int>();
	}
	std::cout << "Test OK.\n";
	return 0;
}
//////////////////////////////////////////////////////////////////////////


int main(int argc, char* argv[])
{
	using namespace DesignMode;
	using namespace GRAPHTREE;
	
	std::cout << "Do mode/C++ ...\n";
	bool is_test_memory = true;
	bool is_test_normaltree = true;
	std::string fst_tree = "fst.text";
	if (is_test_memory)
	{
		HashListTest();
	}
	if (is_test_normaltree)
	{
		NormalTreeRun(argc, argv);
	}
	
	int mode_order = 0;
	if (argc > 2)
	{
		if (strcmp(argv[1], "-t") == 0)
		{
			mode_order = std::atoi(argv[2]);
		}
	}else{
		std::cerr << "Parameter is not enough \n";
		return -1;
	}
	ModeType mode_type = Ornament;
	switch(mode_order){
	case 1:
		mode_type = Singleton;
		DoModePerform(mode_type);
		break;
	case 2:
		mode_type = Factory;
		DoModePerform(mode_type);
		break;
	case 3:
		mode_type = Proxy;
		DoModePerform(mode_type);
		break;
	case 4:
		mode_type = Adapter;
		DoModePerform(mode_type);
		break;
	case 5:
		mode_type = Ornament;
		DoModePerform(mode_type);
		break;
	default:
		std::cerr << "Test type can not be supported, error !\n";
		return MODE_TEST_FAIL;
		break;
	}
	
	return 0;
}

